package com.atguigu.tingshu.album.service.impl;

import com.atguigu.tingshu.album.config.VodConstantProperties;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackStatMapper;
import com.atguigu.tingshu.album.service.AlbumInfoService;
import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.UploadFileUtil;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.album.AlbumTrackListVo;
import com.atguigu.tingshu.vo.album.TrackInfoVo;
import com.atguigu.tingshu.vo.album.TrackListVo;
import com.atguigu.tingshu.vo.album.TrackMediaInfoVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qcloud.vod.VodUploadClient;
import com.qcloud.vod.model.VodUploadRequest;
import com.qcloud.vod.model.VodUploadResponse;
import io.swagger.v3.oas.models.security.SecurityScheme;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo> implements TrackInfoService {

    @Autowired
    private AlbumInfoMapper albumInfoMapper;

    @Autowired
    private TrackInfoMapper trackInfoMapper;

    @Autowired
    private TrackStatMapper trackStatMapper;

    @Autowired
    private AlbumInfoService albumInfoService;

    @Autowired
    private VodService vodService;

    @Autowired
    private VodConstantProperties vodConstantProperties;

    @Autowired
    private UserInfoFeignClient userInfoFeignClient;

    /**
     * 上传音轨文件
     * 该方法使用VodUploadClient来处理音轨文件的上传
     * 它首先创建一个VodUploadClient实例，然后准备上传请求，
     * 包括设置媒体文件的临时路径最后，它发送上传请求并处理响应
     *
     * @param file 要上传的音轨文件，类型为MultipartFile
     * @return 包含上传文件ID和媒体URL的Map<String, Object>
     */
    @Override
    public Map<String, Object> uploadTrack(MultipartFile file) {
        // 创建VodUploadClient实例
        VodUploadClient vodUploadClient = new VodUploadClient(vodConstantProperties.getSecretId(), vodConstantProperties.getSecretKey());
        // 初始化上传请求对象
        VodUploadRequest vodUploadRequest = new VodUploadRequest();
        // 获取文件的临时保存路径
        String tempPath = UploadFileUtil.uploadTempPath(vodConstantProperties.getTempPath(), file);
        // 设置上传请求的媒体文件路径
        vodUploadRequest.setMediaFilePath(tempPath);

        try {
            // 执行文件上传并获取响应
            VodUploadResponse response = vodUploadClient.upload(vodConstantProperties.getRegion(), vodUploadRequest);
            // 记录上传成功的文件ID
            log.info("Upload FileId = {}" + response.getFileId());
            // 创建一个HashMap来存储上传结果
            Map<String, Object> map = new HashMap<>();
            // 将文件ID和媒体URL放入结果map中
            map.put("mediaFileId", response.getFileId());
            map.put("mediaUrl", response.getMediaUrl());
            // 返回数据
            return map;
        } catch (Exception e) {
            // 记录上传过程中的错误
            log.error("Upload File Error", e);
        }

        // 返回一个空的HashMap，因为当前实现中没有处理上传结果
        return new HashMap<>();
    }

    /**
     * 保存声音信息
     *
     * @param trackInfoVo 声音信息的Vo对象，包含需要保存的声音的基本信息
     * @param userId      用户ID，用于记录哪个用户保存了该声音
     *                    <p>
     *                    该方法的主要功能包括：
     *                    1. 将TrackInfoVo对象的属性复制到TrackInfo对象中
     *                    2. 设置声音的用户ID和顺序号
     *                    3. 获取并设置声音的媒体信息
     *                    4. 插入声音信息到数据库
     *                    5. 保存声音的统计信息
     *                    6. 更新专辑的声音数量
     *                    <p>
     *                    注意：该方法使用了事务管理，遇到异常时会回滚事务
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveTrackInfo(TrackInfoVo trackInfoVo, Long userId) {
        // 创建一个新的TrackInfo对象，并将TrackInfoVo对象的属性复制到该对象中
        TrackInfo trackInfo = new TrackInfo();
        BeanUtils.copyProperties(trackInfoVo, trackInfo);
        // 设置声音的用户ID
        trackInfo.setUserId(userId);

        // 根据专辑ID获取专辑信息
        AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfoVo.getAlbumId());
        // 设置声音的顺序号为专辑中现有声音数量加1
        trackInfo.setOrderNum(albumInfo.getIncludeTrackCount() + 1);

        // 获取声音的媒体信息
        TrackMediaInfoVo mediaInfo = vodService.getMediaInfo(trackInfoVo.getMediaFileId());
        // 设置声音的媒体URL、类型、大小和时长
        trackInfo.setMediaUrl(mediaInfo.getMediaUrl());
        trackInfo.setMediaType(mediaInfo.getType());
        trackInfo.setMediaSize(mediaInfo.getSize());
        trackInfo.setMediaDuration(mediaInfo.getDuration());

        // 将声音信息插入数据库
        trackInfoMapper.insert(trackInfo);

        // 保存声音的播放、收藏、点赞和评论统计信息
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PLAY);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COLLECT);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PRAISE);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COMMENT);

        // 更新专辑的轨迹数量
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() + 1);
        this.albumInfoMapper.updateById(albumInfo);
    }

    public void saveTrackStat(Long trackId, String statPlay) {
        // 创建声音统计对象
        TrackStat trackStat = new TrackStat();
        // 赋值
        trackStat.setTrackId(trackId);
        trackStat.setStatType(statPlay);
        trackStat.setStatNum(new Random().nextInt(1000));
        this.trackStatMapper.insert(trackStat);
    }

    /**
     * 获取当前用户的声音列表
     *
     * @param page
     * @param trackInfoQuery
     * @return
     */
    @Override
    public IPage<TrackListVo> getUserTrackPage(Page<TrackListVo> page, TrackInfoQuery trackInfoQuery) {
        return trackInfoMapper.selectUserTrackPage(page, trackInfoQuery);
    }

    /**
     * 根据ID删除声音信息
     * 此方法不仅删除声音，还会更新相关专辑的声音数量，并删除与声音关联的统计信息和流媒体文件
     *
     * @param id 曲目ID
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeTrackInfoById(Long id) {
        // 获取声音信息
        TrackInfo trackInfo = this.getById(id);
        // 删除声音信息
        this.removeById(id);

        // 获取专辑信息
        AlbumInfo albumInfo = this.albumInfoService.getById(trackInfo.getAlbumId());
        // 更新专辑的声音数量
        int includeTrackCount = albumInfo.getIncludeTrackCount() - 1;
        albumInfo.setIncludeTrackCount(includeTrackCount);
        // 更新专辑信息
        albumInfoService.updateById(albumInfo);

        // 删除声音的统计信息
        trackStatMapper.delete(new LambdaQueryWrapper<TrackStat>().eq(TrackStat::getTrackId, id));

        // 更新声音编号
        trackInfoMapper.updateTrackNum(trackInfo.getAlbumId(), trackInfo.getOrderNum());

        // 删除流媒体文件
        vodService.removeMediaFileById(trackInfo.getMediaFileId());
    }

    /**
     * 根据ID回显声音信息
     *
     * @param id
     * @return
     */
    @Override
    public TrackInfo getTrackInfo(Long id) {
        // 调用mapper层的方法
        return trackInfoMapper.selectById(id);
    }

    /**
     * 根据Id修改声音信息
     * 当声音信息中的媒体文件ID发生变化时，从VOD服务中更新媒体信息，并删除旧的媒体文件
     *
     * @param id          声音信息的ID
     * @param trackInfoVo 包含更新后声音信息的VO对象
     */
    @Override
    public void updateTrackInfo(Long id, TrackInfoVo trackInfoVo) {
        // 根据ID获取原有的声音信息
        TrackInfo trackInfo = this.getById(id);
        // 保存旧的媒体文件ID
        String oldMediaFileId = trackInfo.getMediaFileId();
        // 将更新后的声音信息复制到实体对象中
        BeanUtils.copyProperties(trackInfoVo, trackInfo);

        // 检查媒体文件ID是否发生变化
        if (!oldMediaFileId.equals(trackInfoVo.getMediaFileId())) {
            // 如果媒体文件ID发生变化，从VOD服务中获取新的媒体信息
            TrackMediaInfoVo mediaInfo = vodService.getMediaInfo(trackInfoVo.getMediaFileId());
            // 如果获取不到媒体信息，抛出异常
            if (null == mediaInfo) {
                throw new GuiguException(ResultCodeEnum.VOD_FILE_ID_ERROR);
            }
            // 更新声音信息中的媒体URL、大小、类型和时长
            trackInfo.setMediaUrl(mediaInfo.getMediaUrl());
            trackInfo.setMediaSize(mediaInfo.getSize());
            trackInfo.setMediaType(mediaInfo.getType());
            trackInfo.setMediaDuration(mediaInfo.getDuration());
            // 删除旧的媒体文件
            vodService.removeMediaFileById(oldMediaFileId);
        }

        // 更新数据库中的声音信息
        this.updateById(trackInfo);
    }

    /**
     * 根据专辑Id获取到专辑对应的声音列表
     *
     * @param page
     * @param albumId
     * @param userId
     * @return
     */
    @Override
    public IPage<AlbumTrackListVo> findAlbumTrackPage(Page<AlbumTrackListVo> page, Long albumId, Long userId) {
        // 根据专辑Id 获取到声音合集
        IPage<AlbumTrackListVo> pageInfo = trackInfoMapper.selectAlbumTrackPage(page, albumId);
        // 判断用户是否需要付费， 设置是否需要付费字段 isShowPaidMark
        AlbumInfo albumInfo = this.albumInfoMapper.selectById(albumId);
        Assert.notNull(albumInfo, "专辑对象不能为空");
        // 判断用户是否登录
        if (null == userId) {
            // 如果用户没有登录，判断专辑是否需要付费
            if (!albumInfo.getPayType().equals(SystemConstant.ALBUM_PAY_TYPE_FREE)) {
                //  先找到免费试听的集数; 先找到专辑对象;
                Integer tracksForFree = albumInfo.getTracksForFree();
                //  select * from track_info where order_num > 5 and album_id = 1429;
                pageInfo.getRecords().stream()
                        .filter(albumTrackListVo -> albumTrackListVo.getOrderNum() > tracksForFree)
                        .forEach(albumTrackListVo -> albumTrackListVo.setIsShowPaidMark(true));
            }
            //  返回数据
            return pageInfo;
        } else {
            //  获取到当前用户对象
            Result<UserInfoVo> userInfoVoResult = this.userInfoFeignClient.getUserInfoVo(userId);
            //  获取数据
            UserInfoVo userInfoVo = userInfoVoResult.getData();
            //  声明一个字段;
            boolean isNeedPaidMark = false;
            //  说明用户登录; 判断当前专辑的类型;
            if (SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(albumInfo.getPayType())) {
                //  判断当前专辑是vip免费; 判断当前用户是否是vip; is_vip字段;
                if ((1 == userInfoVo.getIsVip() && userInfoVo.getVipExpireTime().before(new Date())) || userInfoVo.getIsVip() == 0) {
                    isNeedPaidMark = true;
                }
            } else if (SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(albumInfo.getPayType())) {
                //  当前专辑是付费类型
                isNeedPaidMark = true;
            }
            //  统一处理付费情况：
            if (isNeedPaidMark) {
                //  除去用户购买的专辑或购买的声音之外{user_paid_album，user_paid_track}，要付费！
                //  获取需要付费的声音Id 列表;
                List<AlbumTrackListVo> trackListVoList = pageInfo.getRecords().stream().filter(albumTrackListVo -> albumTrackListVo.getOrderNum() > albumInfo.getTracksForFree()).collect(Collectors.toList());
                List<Long> trackIdNeedPaidList = trackListVoList.stream().map(AlbumTrackListVo::getTrackId).collect(Collectors.toList());
                //  远程调用
                Result<Map<Long, Integer>> mapResult = userInfoFeignClient.userIsPaidTrack(albumId, trackIdNeedPaidList);
                //  获取数据
                Assert.notNull(mapResult, "付费声音结果集为空");
                //  map.put(trackId,1): 当前声音已购买，map.put(trackId,0): 当前声音未购买
                Map<Long, Integer> map = mapResult.getData();
                //  循环遍历
                for (AlbumTrackListVo albumTrackListVo : trackListVoList) {
                    //  设置当前声音是否需要付费;
                    albumTrackListVo.setIsShowPaidMark(map.get(albumTrackListVo.getTrackId()) == 1 ? false : true);
                }
            }
        }
        // 返回集合数据
        return pageInfo;
    }

    /**
     * 更新统计方法
     *
     * @param albumId
     * @param trackId
     * @param statType
     * @param count
     */
    @Transactional
    @Override
    public void updateStat(Long albumId, Long trackId, String statType, Integer count) {
        // 更新声音播放量
        trackInfoMapper.updateTrackStat(trackId, statType, count);
        // 更新专辑播放量
        if (statType.equals(SystemConstant.TRACK_STAT_PLAY)) {
            albumInfoService.updateAlbumStat(albumId, SystemConstant.ALBUM_STAT_PLAY, count);
        }
    }

    /**
     * 获取用户声音分集购买支付列表
     *
     * @param trackId
     * @return
     */
    @Override
    public List<Map<String, Object>> findUserTrackPaidList(Long trackId) {
        // 获取声音对象
        TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
        // 获取专辑对象
        AlbumInfo albumInfo = albumInfoService.getById(trackInfo.getAlbumId());
        // 根据专辑Id获取声音Id集合
        Result<List<Long>> trackIdListResult = userInfoFeignClient.findUserTrackPaidList(trackInfo.getAlbumId());
        Assert.notNull(trackIdListResult, "专辑Id集合不能为空");
        List<Long> trackIdList = trackIdListResult.getData();
        Assert.notNull(trackIdList, "专辑Id集合不能为空");

        // 获取当前专辑并且大于当前声音的全部声音Id
        List<TrackInfo> trackInfoList = trackInfoMapper.selectList(
                new LambdaQueryWrapper<TrackInfo>()
                        .eq(TrackInfo::getAlbumId, trackInfo.getAlbumId())
                        .gt(TrackInfo::getOrderNum, trackInfo.getOrderNum())
                        .select(TrackInfo::getId)
        );
        List<Long> trackIdAllList = trackInfoList.stream().map(TrackInfo::getId).collect(Collectors.toList());
        // 去除已经支付的
        List<Long> trackIdNoReaptList = new ArrayList<>();
        if (CollectionUtils.isEmpty(trackIdList)) {
            trackIdNoReaptList = trackIdAllList;
        } else {
            // 获取没有购买的声音Id
            trackIdNoReaptList = trackIdAllList.stream().filter(itemId -> !trackIdList.contains(itemId)).collect(Collectors.toList());
        }

        // 构造声音分集购买数据列表
        List<Map<String, Object>> list = new ArrayList<>();
        // 需要付款的集数有 19集
        // 本集
        if (trackIdNoReaptList.size() >= 0) {
            Map<String, Object> map = new HashMap<>();
            map.put("name", "本集");
            map.put("price", albumInfo.getPrice());
            map.put("trackCount", 0);
            list.add(map);
        }

        // 后10集
        if (trackIdNoReaptList.size() > 0 && trackIdNoReaptList.size() <= 10) {
            Map<String, Object> map = new HashMap<>();
            int count = trackIdNoReaptList.size();
            BigDecimal price = albumInfo.getPrice().multiply(new BigDecimal(count));
            map.put("name", "后" + trackIdNoReaptList.size() + "集");
            map.put("price", price);
            map.put("trackCount", count);
            list.add(map);
        }
        // 19集
        if (trackIdNoReaptList.size() > 10) {
            Map<String, Object> map = new HashMap<>();
            BigDecimal price = albumInfo.getPrice().multiply(new BigDecimal(10));
            map.put("name", "后10集");
            map.put("price", price);
            map.put("trackCount", 10);
            list.add(map);
        }
        // 后20集
        if (trackIdNoReaptList.size() > 10 && trackIdNoReaptList.size() <= 20) {
            Map<String, Object> map = new HashMap<>();
            int count = trackIdNoReaptList.size();
            BigDecimal price = albumInfo.getPrice().multiply(new BigDecimal(count));
            map.put("name", "后" + trackIdNoReaptList.size() + "集");
            map.put("price", price);
            map.put("trackCount", count);
            list.add(map);
        }
        if (trackIdNoReaptList.size() > 20) {
            Map<String, Object> map = new HashMap<>();
            BigDecimal price = albumInfo.getPrice().multiply(new BigDecimal(20));
            map.put("name", "后20集");
            map.put("price", price);
            map.put("trackCount", 20);
            list.add(map);
        }
        // 后30集
        if (trackIdNoReaptList.size() > 20 && trackIdNoReaptList.size() <= 30) {
            Map<String, Object> map = new HashMap<>();
            int count = trackIdNoReaptList.size();
            BigDecimal price = albumInfo.getPrice().multiply(new BigDecimal(count));
            map.put("name", "后" + trackIdNoReaptList.size() + "集");
            map.put("price", price);
            map.put("trackCount", count);
            list.add(map);
        }
        if (trackIdNoReaptList.size() > 30) {
            Map<String, Object> map = new HashMap<>();
            BigDecimal price = albumInfo.getPrice().multiply(new BigDecimal(30));
            map.put("name", "后30集");
            map.put("price", price);
            map.put("trackCount", 30);
            list.add(map);
        }
        // 后50集
        if (trackIdNoReaptList.size() > 30 && trackIdNoReaptList.size() <= 50) {
            Map<String, Object> map = new HashMap<>();
            int count = trackIdNoReaptList.size();
            BigDecimal price = albumInfo.getPrice().multiply(new BigDecimal(count));
            map.put("name", "后" + trackIdNoReaptList.size() + "集");
            map.put("price", price);
            map.put("trackCount", count);
            list.add(map);
        }
        // 最多购买50集
        if (trackIdNoReaptList.size() > 50) {
            Map<String, Object> map = new HashMap<>();
            BigDecimal price = albumInfo.getPrice().multiply(new BigDecimal(50));
            map.put("name", "后50集");
            map.put("price", price);
            map.put("trackCount", 50);
            list.add(map);
        }
        return list;
    }

    /**
     * 批量获取下单付费声音列表
     *
     * @param trackId
     * @param trackCount
     * @return
     */
    @Override
    public List<TrackInfo> findPaidTrackInfoList(Long trackId, Integer trackCount) {
        // 创建声音集合列表：
        List<TrackInfo> paidTrackInfoList = new ArrayList<>();
        // 1. 先获取到声音对象
        TrackInfo trackInfo = this.getById(trackId);
        // 2. 判断用户购买的集数;
        if (trackCount > 0) {
            // 3. 找到用户已购买的集合列表：
            Result<List<Long>> trackIdListResult = userInfoFeignClient.findUserTrackPaidList(trackInfo.getAlbumId());
            Assert.notNull(trackIdListResult, "用户购买声音Id列表为空");
            List<Long> paidTrackIdList = trackIdListResult.getData();
            // 4. 查询数据
            // 构建查询条件
            LambdaQueryWrapper<TrackInfo> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(TrackInfo::getAlbumId, trackInfo.getAlbumId()).gt(TrackInfo::getOrderNum, trackInfo.getOrderNum());
            if (paidTrackIdList.size() > 0) {
                wrapper.notIn(TrackInfo::getId, paidTrackIdList);
            }
            // 限制查询数量：
            wrapper.last(" limit " + trackCount);
            // 接收数据
            paidTrackInfoList = trackInfoMapper.selectList(wrapper);
        } else {
            paidTrackInfoList.add(trackInfo);
        }
        // 返回集合列表
        return paidTrackInfoList;
    }

}
